#pragma pack(4)

#include "gsyncer.h"
#include "usrlib.h"
#include <unistd.h>
#include <cstdlib>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <strings.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <iostream>
using namespace std;

#define SA struct sockaddr  

int tcp_listen(int port);
void ctrl_link(int sock_fd);
void update_link(int sock_fd);
void file_copy(int des, int src);
void file_send(int det, int src);
void change_path(const char* src, 
		string& det, const char* usr);

//string server_root;

int main(int argc, char *argv[])
{
//    if(argc==2)
//	server_root=string(argv[1]);
//    else 
//	server_root=string("/gsync");
    if ( fork() == 0 )
		tcp_listen(SRV_CTL_PT);
	else
		tcp_listen(SRV_TSF_PT);

}

void file_send(int det, int src)
{
	int res;
	char buf[BUF_SIZ];

	while( (res = readn(src,buf,BUF_SIZ)) > 0 )
	{
		Writen(det,&res,sizeof(int));
		Writen(det,buf,res);
	}

	if( res == 0 )
	{
		Writen(det,&res,sizeof(int));
		return;
	} 
	else
	{
		perror("read");
		exit(-1);
	}
}

int tcp_listen(int port)
{
	int fd_listen,fd_connect;
	struct sockaddr_in myaddr;
	
	bzero (&myaddr,sizeof(myaddr));
	myaddr.sin_family = AF_INET;
	myaddr.sin_port = htons(port);
	myaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	fd_listen = socket (AF_INET,SOCK_STREAM,0);
	if( fd_listen == -1 )
	{
		perror("tcp process : socket error");
		exit(1);
	}

	//change add on flag, so the address an reuse immeditly after server closed
	int on = 1;
	int ret = setsockopt( fd_listen, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
	
	if( bind(fd_listen,(SA*)&myaddr,sizeof(myaddr)) < 0 )
	{
		perror("tcp process : bind error");
		exit(1);
	}

	if( listen(fd_listen,20) < 0 )
	{
		perror("tcp process : listen error");
		exit(1);
	}

	while(true)
	{
		if( (fd_connect = accept (fd_listen,NULL,NULL)) == -1 )
		{
			if( errno == EINTR )
			{
				continue;
			} 
			else
			{
				perror("tcp process : accept error");
				exit(1);
			}
		}

		if( fork() == 0 )
		{
			close(fd_listen);

			if( port == SRV_CTL_PT )
			{
				ctrl_link(fd_connect);
			}
			else
			{
				update_link(fd_connect);
			}
			exit(0);
		}

		close(fd_connect);
	}
	exit(0);
}

void ctrl_link(int sock_fd)
{
	USRLIB usrlib;
	struct usr_info info;
	int res;

	if( readn(sock_fd,&info,sizeof(struct usr_info)) < 0  )
	{
		perror("read");
		exit(-1);
	}

	if( info.type == REG )
	{
		//用户注册
	    cout<<"user reg"<<endl;
		int8_t reply ;

		//加入数据库 建立用户文件夹
		reply = usrlib.add_usr(info.usr,info.pwd);	

		string usr_dir = SRV_ROOT;
		usr_dir +=  "/";
		usr_dir += info.usr;

		if(reply == 0)
		{
			if( mkdir(usr_dir.c_str(),0766) )
			{
				perror("mkdir");
				exit(1);
			}
		}

		//返回登录结果  -1表示用户名已存在 0表示注册成功
		if( writen(sock_fd,&reply,sizeof(int8_t)) < 0 )
		{
			perror("write");
			exit(-1);
		}

		if( reply == -1 )
		{
			exit(1);
		}

		return;
	} 
	else
	{
		//用户登录
		int8_t reply;
		cout<<"user log in"<<endl;
		//查找数据库 并返回结果给客户端
		reply = usrlib.log_in(info.usr,info.pwd);

		if( writen(sock_fd,&reply,sizeof(int8_t)) < 0 )
		{
			perror("write");
			return;
		}

		//若登录失败  进程关闭
		if( reply != 0 )
		{
			return;
		}

		//打包传给客户端
		int fd_zip;
		string zipfile = SRV_ROOT;
		zipfile += "/";
		zipfile += info.usr;
		string flag_f = zipfile + "/.flag";
		zipfile += "/gsyncer.zip";

		//测试.flag文件是否存在 如果存在  说明需要同步  不存在说明不需要同步
		int fd_flag;
		reply = 1;
		if((fd_flag = open(flag_f.c_str(),O_RDONLY)) < 0 )
		{
			reply = 0;
		}

		close(fd_flag);
		
		//告之客户端是否需要同步 1表示需要 0表示不需要
		if( writen(sock_fd,&reply,sizeof(int8_t)) < 0 )
		{
			perror("write");
			return;
		}


		if(reply == 1)
		{
			if( (fd_zip = open(zipfile.c_str(),O_RDONLY)) < 0 )
			{
				perror("open");
				return;
			}

			file_send(sock_fd,fd_zip);
			close(fd_zip);
			unlink(flag_f.c_str());
		}

		unlink(zipfile.c_str());
	}

	string new_path;
	struct event_info e_info;
	bool changed = false;

	while( true )
	{
		if( readn(sock_fd,&e_info,sizeof(e_info)) <= 0 )
		{
			if( res == -1 )
			{
				perror("read");
				exit(-1);
			}
			else
				e_info.type = EXIT;

		}

		if( e_info.type == CREATE_F )
		{
			changed = true;
			change_path(e_info.path,new_path,info.usr);
			//cout<<"e_info.path "<<e_info.path<<endl;
			cout<<"create file  "<<new_path<<endl;
			int fd = open(new_path.c_str(),O_RDWR|O_CREAT,0766);
			close(fd);			
		} 
		else if(e_info.type ==CREATE)
		{
			changed = true;
		    change_path(e_info.path,new_path,info.usr);
		    //cout<<"e_info.path "<<e_info.path<<endl;
		    cout<<"create  dir "<<new_path<<endl;
		    if ( mkdir(new_path.c_str(),0766) < 0)
			{
				cout <<"create dir failed "<< new_path << endl;
				perror("mkdir");
			//	exit(-1);
		    }
		}
		else if( e_info.type == DEL_F  )
		{
			changed = true;
			change_path(e_info.path,new_path,info.usr);
			//cout<<"e_info.paht "<<e_info.path<<endl;
			cout<<"rm file "<<new_path<<endl;
			if (unlink(new_path.c_str()) < 0)
			{
				perror("unlink");
				cout<<"rm file failed "<<new_path<<endl;
				//	exit(-1);
			}
		}
		else if(e_info.type == DEL_D)
		{
			changed = true;
			change_path(e_info.path,new_path,info.usr);
			//cout<<"e_info.paht "<<e_info.path<<endl;
			cout<<"rm dir "<<new_path<<endl;
			string cmd = "rm -rf \"";
			cmd += new_path;
			cmd+="\"";
			cout<<cmd<<endl;
			system(cmd.c_str());
		}
		else if( e_info.type == UP_F)
		{
			changed = true;
			uint8_t answer = 1;
			cout<<"e_info.path "<<e_info.path<<endl;
			cout<<" update file "<<endl;
			change_path(e_info.path,new_path,info.usr);
			/*int i;
			for(i=new_path.length()-1;i>=0;i--){
			    if(new_path[i]=='/')
				break;
			}
			new_path = new_path.substr(0,i+1);
			//cout<<new_path<<endl;
			
			if( opendir(new_path.c_str()) == NULL){
			    mkdir(new_path.c_str(),0766);
			}
			*/    
			Writen(sock_fd,&answer,sizeof(uint8_t));
		}
		else if(e_info.type==UP_D)
		{
			changed = true;
		   	uint8_t answer = 1;
			cout<<"e_info.path "<<e_info.path<<endl;
			cout<<" update dir"<<endl;
			change_path(e_info.path,new_path,info.usr);
			Writen(sock_fd,&answer,sizeof(uint8_t));
		}
		else if(e_info.type == EXIT)
		{
			//判断当前目录是否为空
			string name_temp = SRV_ROOT;
			name_temp += "/";
			name_temp += info.usr;

			DIR *p_dir = opendir(name_temp.c_str());
			if(p_dir == NULL)
			{
				perror("opendir");
				return;
			}

			//先读.  ..
			readdir(p_dir);
			readdir(p_dir);

			if(readdir(p_dir) == NULL)
			{
				//如果当前目录为空
				//则不需要压缩  也不用创建.flag
				closedir(p_dir);
				cout<< info.usr << " exit\n";
				break;
			}
			closedir(p_dir);

			if( chdir(name_temp.c_str()) < 0 )
			{
				perror("chdir");
				return;
			}
			
		
			//如果文件被该变  则创建.flag 并且打包
 			if(changed)
			{
				int flag_fd;
				if( (flag_fd = open(".flag",O_RDONLY|O_CREAT,0766)) < 0)
				{
					perror("create .flag");
					return;
				}
				close(flag_fd);
				
				//重新打包zip
				system("zip -r gsyncer.zip .");
			}

		    cout<< info.usr << " exit\n";
		    break;
		}
		else
		{
			cout << "defult:" << (int)e_info.type << endl;
		}
	}
	close(sock_fd);
}

void change_path(const char* src, string& det, const char* usr)
{
	det = SRV_ROOT;

	int  i,sum=0;
	for( i = 0 ;  ; i++ )
	{
		if(src[i] == '/')
		{
			sum++;
		}
		if( sum == 4 )
		{
			break;
		}
	}

	det += src+i;
}

void update_link(int sock_fd)
{
	USRLIB usrlib;
	struct update_head head;
	if( readn(sock_fd,&head,sizeof(head)) < 0 )
	{
		perror("read");
		exit(-1);
	}

	//验证用户ID
	int res = usrlib.log_in(head.usr,head.pwd);
	if( res != 0 )
	{
		cerr << "update_link login failed" << endl;
		exit(1);
	}

	if( head.type == UP_F )
	{
		string new_path;
		change_path(head.path,new_path,head.usr);

		int fd_temp = open(new_path.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0766);
		if( fd_temp < 0 )
		{
			cout << new_path << endl;
			perror("open");
			exit(-1);
		}
		cout<<"head.path "<<head.path<<endl;
		cout<<"file update "<<new_path<<endl;
		file_copy(fd_temp,sock_fd);
		close(fd_temp);
	}
	else if(head.type == UP_D)
	{
	    string new_path;
	    change_path(head.path,new_path,head.usr);
	    
	    int fd_temp = open(new_path.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0766);
	    if( fd_temp < 0 )
		{
		    cout << new_path << endl;
		    perror("open");
		    exit(-1);
		}
	    cout<<"head.path "<<head.path<<endl;
	    cout<<"dir update "<<new_path<<endl;
	    file_copy(fd_temp,sock_fd);
	    close(fd_temp);
	    int i;
	    for(i=new_path.length()-1;i>=0;i--){
		if(new_path[i]=='/')
		    break;
	    }
	    string path = new_path.substr(0,i+1);
	    chdir(path.c_str());
	    string cmd ="unzip -q ";
	    cmd+=new_path;
	    cmd+=" ; rm -f ";
	    cmd+=new_path;
	    cout<<cmd<<endl;
	    system(cmd.c_str());
	    chdir("-");
		//system("unzip -q gsyncer.zip ; rm gsyncer.zip");

	}
	else if	(head.type == EXIT)
	{
	    //exit
	}

    close(sock_fd);
}

void file_copy(int des, int src)
{
	int res;
	char buf[BUF_SIZ];

	while( (res = readn(src,buf,BUF_SIZ)) > 0 )
	{
		Writen(des,buf,res);
	}

	if( res == 0 )
	{
		return;
	} 
	else
	{
		perror("read");
		exit(-1);
	}
}
